package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"
)

const PORT = 12730
const HOST = "localhost"

var fake = flag.Bool("fake", false, "测试用的伪装模式")

func main() {
	flag.Parse()

	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", HOST, PORT))
	Err(err)
	conn, err := net.ListenUDP("udp", addr)
	Err(err)
	defer conn.Close()

	if !(*fake) {
		initAudio()
		defer input.Close()
	}
	println("PID", os.Getpid())
	err = serve(conn)
	if err != nil {
		println("Exit:", err.Error())
	}

}

func Err(err error) {
	if err != nil {
		panic(err)
	}
}

func serve(conn *net.UDPConn) error {
	exitChan := make(chan os.Signal)
	go waitSignal(exitChan)
	var buf [128]byte
	trMap := sync.Map{}
LOOP:
	for {
		conn.SetReadDeadline(time.Now().Add(time.Second * 5))
		n, raddr, err := conn.ReadFromUDP(buf[:])
		if err != nil {
			e1, ok := err.(net.Error)
			if ok && !e1.Timeout() {
				return err
			}

		}
		msg := buf[0:n]
		switch strings.TrimSpace(string(msg)) {
		case "Begin":
			println("Begin work.", raddr.String())
			var tr *Translater
			val, ok := trMap.Load(raddr.String())
			if ok {
				tr = val.(*Translater)
				if rdLocker.TryLock() {
					rdLocker.Unlock()
					tr.Close()
				} else {
					tr.JustEnd()
					tr.Close()
					trMap.Delete(raddr.String())
				}
			}
			tr = &Translater{}
			trMap.Store(raddr.String(), tr)
			tr.Begin(conn, raddr)

		case "End":
			println("End work.", raddr.String())
			tr, ok := trMap.Load(raddr.String())
			if ok {
				//println("end")
				tr.(*Translater).End()
				//println("close")
				tr.(*Translater).Close()
				trMap.Delete(raddr.String())
			}
		}
		select {
		case <-exitChan:
			println("Exit LOOP")
			break LOOP
		default:
		}
	}
	return nil
}

type Translater struct {
	conn    *net.UDPConn
	raddr   *net.UDPAddr
	ctrl    chan int8
	wavpath string
}

func (p *Translater) Begin(conn1 *net.UDPConn, raddr1 *net.UDPAddr) {
	p.conn = conn1
	p.raddr = raddr1
	if !(*fake) {
		p.ctrl = make(chan int8)
		os.MkdirAll("/tmp/wav16k", os.ModePerm)
		rname := rand.Uint32()
		p.wavpath = fmt.Sprintf("/tmp/wav16k/a%v.wav", rname)

		go startRecord(p.wavpath, p.ctrl)
	}

}

func (p *Translater) Close() {
	close(p.ctrl)
	for _ = range p.ctrl {
	}
}

func (p *Translater) JustEnd() {
	if !(*fake) {
		p.ctrl <- 1
		<-p.ctrl
	}
}

func (p *Translater) End() {
	//p.fakeSend()
	if *fake {
		p.fakeSend()
		return
	}

	if rdLocker.TryLock() {
		rdLocker.Unlock()
	} else {
		p.ctrl <- 1
		<-p.ctrl
	}
	// os.MkdirAll("/tmp/wav16k", os.ModePerm)
	// rname := rand.Uint32()
	// wavpath := fmt.Sprintf("/tmp/wav16k/a%v.wav", rname)
	r, err := http.PostForm("http://localhost:15060/api/asr", url.Values{"name": {p.wavpath}})
	if err != nil {
		p.send("")
		println("error postform:", err.Error())
		return
	}
	defer r.Body.Close()
	defer os.Remove(p.wavpath)

	var data [64]byte
	n, err := r.Body.Read(data[:])
	if n == 0 {
		p.send("")
		println("error body.read:", err.Error())
		return
	}
	//println("result:", string(data[0:n]))
	p.send(strings.TrimSpace(string(data[0:n])))

}

func (p *Translater) fakeSend() {
	result := "message fake."

	p.conn.WriteToUDP([]byte(result), p.raddr)

}

func (p *Translater) send(s string) {
	p.conn.WriteToUDP([]byte(s), p.raddr)

}

func waitSignal(ce chan<- os.Signal) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGABRT, syscall.SIGQUIT)
	s := <-c
	log.Println("Exit Signal:", s)
	ce <- s

}
